/*
 * Copyright 2015 泛泛o0之辈
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.jfast.framework.jdbc.orm.sql;

import cn.jfast.framework.base.util.ClassUtils;
import cn.jfast.framework.base.util.StringUtils;
import cn.jfast.framework.jdbc.db.ConnectionFactory;
import cn.jfast.framework.jdbc.orm.Executor;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.*;

/**
 * Created by Think on 2015/5/26.
 */
public class MethodSql extends Executor {

    private String methodName;
    private ResultSet rs;

    private String SELECT = "select";
    private String UPDATE = "update";
    private String DELETE = "delete";
    private String INSERT = "insert";
    private String ALL = "All";
    private String INTO = "Into";
    private String VALUES = "Values";
    private String BY = "By";
    private String FROM = "From";
    private String SET = "Set";
    private String ASC = "Asc";
    private String DESC = "Desc";

    public MethodSql(Type[] paramTypes,
                     String[] paramNames,
                     Object[] args,
                     String methodName,
                     Type returnType,
                     Method method) throws IllegalAccessException, SQLException, ClassNotFoundException {
        this.method = method;
        this.paramNames = paramNames;
        this.paramTypes = paramTypes;
        this.args = args;
        this.methodName = methodName;
        this.returnType = returnType;
        conn = ConnectionFactory.getThreadLocalConnection();
        this.paramMaps = wrapParam();
    }

    @Override
    public Object execute() throws IllegalAccessException, SQLException, NoSuchFieldException, InstantiationException {

        Object reObj = null;
        String sql = parseSql();
        ps = conn.prepareStatement(sql);
        fillPreparedStatement();
        if(sql.startsWith("SELECT")) {
            rs = ps.executeQuery();

            ResultSetMetaData meta = rs.getMetaData();
            int column = meta.getColumnCount();
            String[] columns = new String[column];
            int[] columntypes = new int[column];

            for (int i = 0; i < column; i++) {
                columns[i] = meta.getColumnName(i + 1);
                columntypes[i] = meta.getColumnType(i + 1);
            }

            if (returnType instanceof ParameterizedType) {        //如果是带泛型类型的参数
                ParameterizedType parameterizedType = (ParameterizedType) returnType;
                Type basicType = parameterizedType.getRawType();
                Type[] paramTypes = parameterizedType.getActualTypeArguments();

                if (basicType == List.class) {
                    Type fType = paramTypes[0];
                    Class<?> fClass = (Class<?>) fType;
                    List result = new ArrayList();
                    while (rs.next()) {
                        if (ClassUtils.isCommonTypeOrWrapper(fClass)) {
                            if (column > 1)
                                throw new SQLException("查询语句需要返回单列数据，但是查询了多个列。");
                            else
                                result.add(rs.getObject(1));
                        } else {
                            Object tempObj = fClass.newInstance();
                            for (int i = 0; i < column; i++) {
                                Field field = fClass.getDeclaredField(columns[i]);
                                if (null != field) {
                                    field.setAccessible(true);
                                    field.set(tempObj,rs.getObject(columns[i]));
                                }
                            }
                            result.add(tempObj);
                        }
                    }
                    reObj = result;
                } else if (basicType == Map.class) {
                    Type fType1 = paramTypes[0];
                    if (fType1 != String.class)
                        throw new SQLException("查询语句返回Map时，泛型第一位必须是String。");
                    Map result = new HashMap();
                    if (rs.next()) {
                        for (int i = 0; i < column; i++) {
                            result.put(columns[i], rs.getObject(columns[i]));
                        }
                        if (rs.next())
                            throw new SQLException("查询语句需要返回Map对象，要求返回一条记录，但是查询到了多条记录。");
                    }
                    reObj = result;
                }
            } else {
                if (ClassUtils.isCommonTypeOrWrapper((Class<?>) returnType)) {
                    if (column > 1)
                        throw new SQLException("查询语句需要返回单列数据，但是查询了多个列。");
                    if (rs.next()) {
                            reObj = rs.getObject(columns[0]);
                        if (rs.next())
                            throw new SQLException("查询语句需要返回单个值，要求返回一条记录，但是查询到了多条记录。");
                    }
                } else if (returnType == List.class) {
                    List result = new ArrayList();
                    while (rs.next()) {
                        Map<String, Object> tempMap = new HashMap<String, Object>();
                        for (int i = 0; i < column; i++) {
                            tempMap.put(columns[i], rs.getObject(columns[i]));
                        }
                        result.add(tempMap);
                    }
                    reObj = result;
                } else if (returnType == Map.class) {
                    Map result = new HashMap();
                    if (rs.next()) {
                        for (int i = 0; i < column; i++)
                            result.put(columns[i], rs.getObject(columns[i]));
                        if (rs.next())
                            throw new SQLException("查询语句需要返回Map对象，要求返回一条记录，但是查询到了多条记录。");
                    }
                    reObj = result;
                } else {
                    Class<?> clazz = (Class<?>) returnType;
                    if (rs.next()) {
                        Object result = clazz.newInstance();
                        for (int i = 0; i < column; i++) {
                            Field field = clazz.getDeclaredField(columns[i]);
                            if (null != field) {
                                field.setAccessible(true);
                                field.set(result, rs.getObject(columns[i]));
                            }
                        }
                        reObj = result;
                    }
                }

            }
        } else {
            if(returnType == void.class)
                ps.executeUpdate();
            else if(returnType == Boolean.class || returnType == Boolean.TYPE)
                reObj = ps.execute();
            else if(returnType == Integer.class || returnType == Integer.TYPE)
                reObj = ps.executeUpdate();
        }
        if(conn.getAutoCommit() == true && !conn.isClosed())
            conn.close();

        return reObj;
    }

    @Override
    public String getSql(){

        String tableName = null;

        StringBuffer sbf = new StringBuffer();

        if(methodName.startsWith(SELECT)) {
            sbf.append("SELECT ");
            //添加 select 域
            String selectField = StringUtils.substringBetween(methodName, SELECT, FROM);
            if(selectField.isEmpty()){
                throw new RuntimeException("方法名："+methodName+" select与From之间必须有字段参数。");
            }
            if(selectField.equals(ALL)){
                sbf.append("* FROM ");
            } else {
                String[] selectFields = StringUtils.splitByUpperCase(selectField);
                for(String field:selectFields){
                    if(!field.equals(""))
                        sbf.append(field.toLowerCase()+",");
                }
                if(selectFields.length > 0)
                    sbf.deleteCharAt(sbf.length()-1).append(" FROM ");
            }
            //添加 from 域
            if(methodName.indexOf(BY) != -1){
                if(methodName.endsWith(BY))
                    throw new RuntimeException(methodName+"不可以以By结尾。");
                tableName = StringUtils.substringBetween(methodName,FROM,BY);
                sbf.append(tableName.toLowerCase()+" ");
                //添加 where 域
                String[] whereFields = null;
                if(methodName.indexOf(ASC) != -1)
                    whereFields = StringUtils.splitByUpperCase(StringUtils.substringBetween(methodName, BY, ASC));
                else if(methodName.indexOf(DESC) != -1)
                    whereFields = StringUtils.splitByUpperCase(StringUtils.substringBetween(methodName, BY, DESC));
                else
                    whereFields = StringUtils.splitByUpperCase(methodName.substring(methodName.indexOf(BY) + 2));
                sbf.append("WHERE");
                for(String whereField:whereFields){
                    if(!whereField.equals("")) {
                        sbf.append("AND "+whereField.toLowerCase() + " = :" + whereField + " ");
                    }
                }
            } else {
                tableName = methodName.substring(methodName.indexOf(FROM)+4);
                sbf.append(tableName.toLowerCase()+" ");
            }

            //添加 order 域
            if(methodName.indexOf(ASC) != -1){
                if(methodName.endsWith(ASC))
                    throw new RuntimeException(methodName+"不可以以ASC结尾。");
                sbf.append("ORDER BY ");
                String[] orderFields = StringUtils.splitByUpperCase(methodName.substring(methodName.indexOf(ASC) + 3));
                for(String order:orderFields)
                    sbf.append(order.toLowerCase()+",");
                if(orderFields.length > 0)
                    sbf.deleteCharAt(sbf.length()-1).append(" ASC");
            } else if(methodName.indexOf(DESC) != -1){
                if(methodName.endsWith(DESC))
                    throw new RuntimeException(methodName+"不可以以DESC结尾。");
                String[] orderFields = StringUtils.splitByUpperCase(methodName.substring(methodName.indexOf(DESC) + 4));
                for(String order:orderFields)
                    sbf.append(order.toLowerCase()+",");
                if(orderFields.length > 0)
                    sbf.deleteCharAt(sbf.length()-1).append(" DESC");
            }
        } else if(methodName.startsWith(UPDATE)) {
            if(methodName.indexOf(SET) == -1)
                throw new RuntimeException("更新语句错误,更新语句必须包含Set字段。");
            if(methodName.indexOf(BY) == -1)
                throw new RuntimeException("更新语句错误,更新语句必须包含By条件。");
            tableName = StringUtils.substringBetween(methodName, UPDATE, SET).toLowerCase();
            sbf.append("UPDATE "+tableName+" SET ");
            //添加 update 域
            String[] updateFields = StringUtils.splitByUpperCase(StringUtils.substringBetween(methodName, SET, BY));
            for(String updateField:updateFields){
                if(!"".equals(updateField))
                    sbf.append(updateField.toLowerCase()+" = :"+updateField+",");
            }
            if(updateFields.length > 0)
                sbf.deleteCharAt(sbf.length()-1).append(" WHERE");

            if(methodName.indexOf(BY) == -1)
                throw new RuntimeException("更新语句错误,更新语句必须有WHERE后条件。");
            if(methodName.endsWith(BY))
                throw new RuntimeException(methodName+"不可以以BY结尾。");
            //添加 where 域
            String[] whereFields = StringUtils.splitByUpperCase(methodName.substring(methodName.indexOf(BY) + 2));
            for(String whereField:whereFields){
                if(!whereField.equals("")) {
                    sbf.append("AND "+whereField.toLowerCase() + " = :" + whereField + " ");
                }
            }
        } else if(methodName.startsWith(DELETE)) {
            if(methodName.indexOf(FROM) == -1)
                throw new RuntimeException("删除语句错误,删除语句必须包含From开头。");
            if(methodName.indexOf(BY) == -1)
                throw new RuntimeException("删除语句错误,删除语句必须以包含By条件。");
            tableName = StringUtils.substringBetween(methodName, FROM, BY).toLowerCase();
            sbf.append("DELETE FROM "+tableName+" WHERE");
            if(methodName.indexOf(BY) == -1)
                throw new RuntimeException("删除语句错误,更新语句必须有WHERE后条件。");
            if(methodName.endsWith(BY))
                throw new RuntimeException(methodName+"不可以以BY结尾。");
            //添加 where 域
            String[] whereFields = StringUtils.splitByUpperCase(methodName.substring(methodName.indexOf(BY) + 2));
            for(String whereField:whereFields){
                if(!whereField.equals("")) {
                    sbf.append("AND "+whereField.toLowerCase() + " = :" + whereField + " ");
                }
            }
        } else if(methodName.startsWith(INSERT)) {
            if(methodName.indexOf("insertInto") == -1)
                throw new RuntimeException("插入语句错误,插入语句必须以insertInto开头。");
            if(methodName.indexOf(VALUES) != -1) {
                tableName = StringUtils.substringBetween(methodName, INTO, VALUES).toLowerCase();
                sbf.append("INSERT INTO " + tableName + " (");
                String[] valueFields = StringUtils.splitByUpperCase(methodName.substring(methodName.indexOf(VALUES) + 6));
                if(valueFields.length == 0)
                    throw new RuntimeException("插入语句不可以以Values结尾。");
                for(String valueField:valueFields){
                    if(!"".equals(valueField))
                        sbf.append(valueField.toLowerCase()+",");
                }
                if(valueFields.length > 0)
                    sbf.deleteCharAt(sbf.length()-1).append(") VALUES (");
                for(String valueField:valueFields){
                    if(!"".equals(valueField))
                        sbf.append(":"+valueField+" ,");
                }
                if(valueFields.length > 0)
                    sbf.deleteCharAt(sbf.length()-1).append(")");
            } else {
                tableName = methodName.substring(methodName.indexOf("Into")+4).toLowerCase();
                sbf.append("INSERT INTO " + tableName + " (");
                for(Map.Entry<String,Object> entry:paramMaps.entrySet()){
                    if(!"".equals(entry.getKey()) && null != entry.getValue())
                        sbf.append(entry.getKey().toLowerCase()+",");
                }
                if(!paramMaps.isEmpty())
                    sbf.deleteCharAt(sbf.length()-1).append(") VALUES (");
                for(Map.Entry<String,Object> entry:paramMaps.entrySet()){
                    if(!"".equals(entry.getKey()) && null != entry.getValue())
                        sbf.append(":"+entry.getKey()+" ,");
                }
                if(!paramMaps.isEmpty())
                    sbf.deleteCharAt(sbf.length()-1).append(")");
            }
        } else {
            throw new RuntimeException("方法名："+methodName+" 无法被解析为SQL。");
        }
        return sbf.toString().replaceAll("null","").replaceFirst("AND", "");
    }

}
